home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 007 / a86v307b.arc / 07FLOAT.DOC < prev    next >
Text File  |  1987-07-13  |  19KB  |  423 lines

  1. CHAPTER 7   THE FLOATING-POINT PROCESSOR                  7-1
  2.  
  3.  
  4. In this chapter, we'll refer to the various Central Processing 
  5. Units (CPUs) as the "86".  Thus "86" refers to either the 8088, 
  6. 8086, 80186, 80286, etc.  We'll refer to the various coprocessors 
  7. as the "87".  Thus "87" refers to either the 8087 or the 287.  
  8.  
  9.  
  10. The 8087 and 287 Coprocessors
  11.  
  12. All IBM-PC's, and most clones, contain a socket for a floating-
  13. point coprocessor.  If you shell out between $80 and $300, and 
  14. plug the appropriate chip into that socket, then a host of 
  15. floating-point instructions is added to the assembly-language 
  16. instruction set.  
  17.  
  18. The original IBM-PC, and the XT, accept the original floating-
  19. point chip, the 8087.  The AT accepts a later update, the 287.  
  20. From a programming standpoint, the two chips are nearly 
  21. identical: the 287 adds the instructions FSETPM and FSTSW AX, and 
  22. ignores the instructions FENI and FDISI.  There is, however, a 
  23. rather nasty design flaw in the 8087, that was corrected in the 
  24. 287.  
  25.  
  26. To understand the flaw, you must understand how the 86 and 87 
  27. work as coprocessors. Whenever the 86 sees a floating-point 
  28. instruction, it communicates the instruction, and any associated 
  29. memory operands, to the 87.  Then the 86 goes on to its next 
  30. instruction, operating in parallel with the 87.  That's OK, so 
  31. long as the following instructions don't do one of the following: 
  32.  
  33.   1. Execute another floating point instruction; or
  34.   2. Try to read the results of the still-executing floating 
  35.      point instruction.  
  36.  
  37. If they do, then you must provide an instruction called WAIT (or 
  38. synonymously FWAIT), which halts the 86 until the 87 is finished. 
  39. For almost all floating-point instructions, it should not be 
  40. necessary to provide an explicit FWAIT; the 86 ought to know that 
  41. it should wait.  For the 8087, it IS necessary to give an 
  42. explicit FWAIT before each floating-point instruction: that is 
  43. the flaw.  
  44.  
  45. Because of the flaw, all assemblers supporting the 8087 will 
  46. silently insert an FWAIT code (hex 9B) before all 87 
  47. instructions, except those few (the FN-instructions other than 
  48. FNOP) not requiring the FWAIT.  A86 provides the switch +F (the F 
  49. must be capitalized), to signal that the 287 is the target 
  50. processor.  A86 also provides the directive ".287", compatible 
  51. with Microsoft's assembler, that you can insert into your 
  52. programs to accomplish the same thing as +F. However, the actions 
  53. taken by A86 and Microsoft when seeing .287 are completely 
  54. disjoint!  To wit: 
  55.  
  56. * A86 ceases outputting FWAIT directives that are unnecessary for 
  57.   the 287.  For reasons beyond my comprehension, Microsoft 
  58.   continues to put them out.  Can someone enlighten me as to why 
  59.   Microsoft is putting out those codes?  
  60.                                                           7-2
  61. * A86 ignores the instructions FENI, FDISI, FNENI, and FNDISI 
  62.   after it sees a .287 directive.  Microsoft continues to 
  63.   assemble these instructions.  
  64.  
  65. * Microsoft recognizes the new 287 instructions, if and only if 
  66.   it sees the .287 directive.  A86 recognizes them even if .287 
  67.   is not given.  In general, I don't attempt to police your 
  68.   instruction usage-- if you use an instruction available on a 
  69.   limited number of processors, I trust that you are programming 
  70.   for one of those processors.  
  71.  
  72. In summary, if your program will be running only on machines with 
  73. a 287, you can give ".287" directive. Your programs will be 
  74. significantly shorter than if they were assembled by Microsoft.  
  75. If you want your programs to run on all machines containing a 
  76. floating-point chip, you should refrain from specifying .287. 
  77.  
  78. WARNING: The most common mistake 87 programmers make is to try to 
  79. read the results of an 87 operation in 86 memory, before the 
  80. results are ready.  At least on my AT, the system often crashes 
  81. when you do this!  If your program runs correctly when single-
  82. stepped, but crashes when set loose, then chances are you need an 
  83. extra explicit FWAIT somewhere. 
  84.  
  85.  
  86. Emulating the 8087 by Software
  87.  
  88. There is a software package provided with many compilers 
  89. (Borland's Turbo C and most Microsoft compilers, for example) 
  90. that emulates the 8087 instruction set.  The emulator is very 
  91. cleverly implemented so that the programmer need not know whether 
  92. a floating-point chip will be available, or whether emulation 
  93. will be necessary.  This is done by having the linker replace all 
  94. floating-point machine instructions with INT calls to certain 
  95. interrupts, dedciated to emulation.  The interrupt handlers 
  96. interpret the operands to the instructions, and emulate the 
  97. 8087. 
  98.  
  99. You can tell A86 that the emulator might be used, by providing a 
  100. +f switch in the invocation line, or in the A86 environment 
  101. variable (make sure the f is lower-case).  Since your program 
  102. will be linked to the emulator, you must be producing an OBJ 
  103. file, not a COM file,  for emulation support to take effect.  
  104. Whenever a floating-point instruction is assembled, A86 will 
  105. generate an external reference at the opcode for the instruction.  
  106. Then, if the emulation package is linked with your program, the 
  107. opcodes will be replaced by the INT calls. If a special non-
  108. emulation module is linked, the opcodes will be left alone, and 
  109. the floating-point instructions will be executed directly. 
  110.                                                           7-3
  111.  
  112. The Floating Point Stack
  113.  
  114. The 87 has its own register set, of 8 floating-point numbers 
  115. occupying 10 bytes each, plus 14 bytes of status and control 
  116. information.  Many of the 87's instructions cause the numbers to 
  117. act like a stack, much like a Hewlett-Packard calculator.  For 
  118. this reason, the numbers are called the floating-point stack.  
  119.  
  120. The standard name for the top element of the floating-point stack 
  121. is either ST or ST(0); the others are named ST(1) through ST(7).  
  122. Thus, for example, the instruction to add stack element number 3 
  123. into the top stack element is usually coded FADD ST,ST(3).  
  124.  
  125. I find this notation painfully verbose.  Especially bad are the 
  126. parentheses, which are hard to type, and which add visual clutter 
  127. to the program.  To alleviate this problem while retaining 
  128. language compatibility, I name my stack elements simply 0 through 
  129. 7.  I recognize ST as a synonym for 0.  I allow expression 
  130. elements to be concatenated; concatenation is the same as 
  131. addition.  Thus, when A86 sees ST(3), it computes 0+3 = 3.  So 
  132. you can code the old way, FADD ST,ST(3), or you can code the 
  133. concise way, FADD 0,3 or simply FADD 3.  
  134.  
  135.  
  136. Floating Point Initializations
  137.  
  138. In general, you use the 87 by loading numbers from 86 memory to 
  139. the 87 stack (using FLD instructions), calculating on the 87 
  140. stack, and storing the results back to 86 memory (using FST and 
  141. FSTP instructions).  There are seven constant numbers built into 
  142. the 87 instruction set: zero, one, Pi, and four logarithmic 
  143. conversion constants.  These can be loaded using the FLD0, FLD1, 
  144. FLDPI, FLDL2T, FLDL2E, FLDLG2, and FLDLN2 instructions.  All 
  145. other constants must be declared in, then loaded from, 86 memory.
  146. Integer constant words and doublewords can be loaded via FILD.
  147. Non-integer constant doubleword, quadwords, and ten-byte numbers 
  148. can be loaded via FLD.
  149.  
  150. A86 allows you to declare constants loaded via FLD as floating-
  151. point numbers, using scientific notation if you like.  As an 
  152. exclusive feature, A86 allows you to use any of the 4 arithmetic 
  153. functions +, -, *, / in expressions involving floating-point 
  154. numbers.  A86 will even do type conversion if one of the two 
  155. operands is given as an integer; though for clarity I recommend 
  156. that you always give floating-point constants with their decimal 
  157. point.
  158.  
  159.  
  160. Built-In Constant Names
  161.  
  162. A86 offers another exclusive feature: the built-in symbols
  163.  
  164.     PI   ratio of circumference to diameter of a circle
  165.     L2T  log base 2 of 10
  166.     L2E  log base 2 of the calculus constant e = 2.71828...
  167.     LG2  log base 10 of 2 
  168.     LN2  natural log (base e) of 2
  169.                                                           7-4
  170.  
  171. You can use these symbols in expressions, to declare useful 
  172. constants.  For example, you can declare the degrees-to-radians 
  173. conversion constant:
  174.  
  175.     DEG_TO_RAD  DT  PI/180.
  176.  
  177.  
  178. Special Immediate FLD Form
  179.  
  180. Yet another exclusive A86 feature is the instruction form
  181. FLD constant.  This form is intended primarily to facilitate 
  182. "fooling around" with the 87 when using D86; but it is also 
  183. useful for quick-and-dirty programs.  For example, the 
  184. instruction FLD 12.3 generates the following sequence of code 
  185. bytes (without explicitly using the local labels given):
  186.  
  187.     CS FLD T[M1]
  188.     JMP >M2
  189.   M1  DT 12.3
  190.   M2:
  191.  
  192. Obviously, this form is not terrifically efficient: you can 
  193. always save the JMP by placing the constant outside of the 
  194. instruction stream; and the CS-override might not be needed.  But 
  195. the form is very, very convenient!
  196.  
  197. NOTE that the preceding 2 sections imply that you can get 
  198. careless and code, for example, FLD PI when you intended FLDPI.  
  199. Though the two are functionally equivalent, the first form takes 
  200. a whopping 17 bytes; and second, only 2 bytes.  Be careful!
  201.  
  202.  
  203.  
  204. Floating Point Operand Types
  205.  
  206. The list of floating point instructions contains a variety of 
  207. operand types.  Here is a brief explanation of those types: 
  208.  
  209. 0  stands for the top element of the floating-point stack.  A 
  210.    synonym for 0 is ST or ST(0).  
  211.  
  212. i  stands for element number i of the floating-point stack.  i 
  213.    can range from 0 through 7.  A synonym for i is ST(i).  
  214.  
  215. mem10r is a 10-byte memory quantity (typically declared with a DT 
  216.    directive) containing a full-precision floating point number.  
  217.    Intel recommends that you NOT store your numbers in full 
  218.    precision; that you use the following double precision format 
  219.    instead.  Full-precision numbers are intended for storage of 
  220.    intermediate results (on the stack); they exist to insure 
  221.    maximum accuracy for calculations on double-precision numbers, 
  222.    which is the official external format of 87 numbers.  
  223.                                                           7-5
  224.  
  225. mem8r is an 8-byte memory quantity (typically declared with a DQ 
  226.    directive) containing a double-precision floating-point 
  227.    number.  This is the best format for floating-point numbers on 
  228.    the 87.  The 87 takes the same amount of time on double-
  229.    precision calculations as it does on single-precision.  The 
  230.    only extra time is the memory access of 4 more bytes; 
  231.    negligible in comparison to the calculation time.  
  232.  
  233. mem4r is a 4-byte quantity (typically defined with a DD 
  234.    directive) containing a single-precision floating-point 
  235.    number.  
  236.  
  237. mem10d is a 10-byte quantity (also defined via DT) containing a 
  238.    special Binary Coded Decimal format recognized by the FBLD and 
  239.    FBSTP instructions.  This format is useful for input and 
  240.    output of floating-point numbers.  
  241.  
  242. mem4i is a 4-byte quantity representing a signed integer in 
  243.    two's-complement notation.  
  244.  
  245. mem2i is a 2-byte quantity representing a signed integer in 
  246.    two's-complement notation.  
  247.  
  248. mem14 and mem94 are 14- and 94-byte buffers containing the 87 
  249.     machine state.  
  250.  
  251.  
  252. Operand Choices in A86
  253.  
  254. The choice of operands for floating-point instructions seems 
  255. inconsistent to me.  For example, to subtract stack i from 0, you 
  256. must provide two operands; to do the equivalent comparison, you 
  257. must provide only one operand.  A86 smooths out these 
  258. inconsistencies by allowing more choices for operands: FADD i is 
  259. equivalent to FADD 0,i.  FCOM 0,i is equivalent to FCOM i.  The 
  260. same holds for the other main arithmetic instructions.  FXCH 0,i 
  261. and FXCH i,0 are allowed.  So if you wish to retain compatibility 
  262. with other assemblers, you should use their more restrictive 
  263. instruction list, not the following one.  
  264.  
  265.  
  266. The 87 Instruction Set
  267.  
  268. Following is the 87 instruction set.  The "w" in the opcode field 
  269. is the FWAIT opcode, hex 9B, which is suppressed if .287 is 
  270. selected.  Again, "0", "1", and "i" stand for the associated 
  271. floating point stack registers, not constant numbers!  Constant 
  272. numbers in the descriptions are given with decimal points: 0.0, 
  273. 1.0, 2.0, 10.0.  
  274.                                                           7-6
  275.  
  276.  w D9 F0     F2XM1           0 := (2.0 ** 0) - 1.0
  277.  w D9 E1     FABS            0 := |0|
  278.  w DE C1     FADD            1 := 1 + 0, pop
  279.  w D8 C0+i   FADD i          0 := i + 0
  280.  w DC C0+i   FADD i,0        i := i + 0
  281.  w D8 C0+i   FADD 0,i        0 := i + 0
  282.  w D8 /0     FADD mem4r      0 := 0 + mem4r
  283.  w DC /0     FADD mem8r      0 := 0 + mem8r
  284.  w DE C0+i   FADDP i,0       i := i + 0, pop
  285.  w DF /4     FBLD mem10d     push, 0 := mem10d
  286.  w DF /6     FBSTP mem10d    mem10d := 0, pop
  287.  w D9 E0     FCHS            0 := -0
  288. 9B DB E2     FCLEX           clear exceptions
  289.  w D8 D1     FCOM            compare 0 - 1
  290.  w D8 D0+i   FCOM 0,i        compare 0 - i
  291.  w D8 D0+i   FCOM i          compare 0 - i
  292.  w D8 /2     FCOM mem4r      compare 0 - mem4r
  293.  w DC /2     FCOM mem8r      compare 0 - mem8r
  294.  w D8 D9     FCOMP           compare 0 - 1, pop
  295.  w D8 D8+i   FCOMP 0,i       compare 0 - i, pop
  296.  w D8 D8+i   FCOMP i         compare 0 - i, pop
  297.  w D8 /3     FCOMP mem4r     compare 0 - mem4r, pop
  298.  w DC /3     FCOMP mem8r     compare 0 - mem8r, pop
  299.  w DE D9     FCOMPP          compare 0 - 1, pop both
  300.  w D9 F6     FDECSTP         decrement stack pointer
  301.  w DB E1     FDISI           disable interrupts (.287 ignore)
  302.  w DE F9     FDIV            1 := 1 / 0, pop
  303.  w D8 F0+i   FDIV i          0 := 0 / i
  304.  w DC F8+i   FDIV i,0        i := i / 0
  305.  w D8 F0+i   FDIV 0,i        0 := 0 / i
  306.  w D8 /6     FDIV mem4r      0 := 0 / mem4r
  307.  w DC /6     FDIV mem8r      0 := 0 / mem8r
  308.  w DE F8+i   FDIVP i,0       i := i / 0, pop
  309.  w DE F1     FDIVR           1 := 0 / 1, pop
  310.  w D8 F8+i   FDIVR i         0 := i / 0
  311.  w DC F0+i   FDIVR i,0       i := 0 / i
  312.  w D8 F8+i   FDIVR 0,i       0 := i / 0
  313.  w D8 /7     FDIVR mem4r     0 := mem4r / 0
  314.  w DC /7     FDIVR mem8r     0 := mem8r / 0
  315.  w DE F0+i   FDIVRP i,0      i := 0 / i, pop
  316.  w DB E0     FENI            enable interrupts (.287 ignore)
  317.  w DD C0+i   FFREE i         empty i
  318.  w DE /0     FIADD mem2i     0 := 0 + mem4i
  319.  w DA /0     FIADD mem4i     0 := 0 + mem2i
  320.  w DE /2     FICOM mem2i     compare 0 - mem2i
  321.  w DA /2     FICOM mem4i     compare 0 - mem4i
  322.  w DE /3     FICOMP mem2i    compare 0 - mem2i, pop
  323.  w DA /3     FICOMP mem4i    compare 0 - mem4i, pop
  324.  w DE /6     FIDIV mem2i     0 := 0 / mem2i
  325.  w DA /6     FIDIV mem4i     0 := 0 / mem4i
  326.  w DE /7     FIDIVR mem2i    0 := mem2i / 0
  327.  w DA /7     FIDIVR mem4i    0 := mem4i / 0
  328.  w DF /0     FILD mem2i      push, 0 := mem2i
  329.  w DB /0     FILD mem4i      push, 0 := mem4i
  330.  w DF /5     FILD mem8i      push, 0 := mem8i
  331.  w DE /1     FIMUL mem2i     0 := 0 * mem2i
  332.  w DA /1     FIMUL mem4i     0 := 0 * mem4i
  333.                                                           7-7
  334.  
  335.  w D9 F7     FINCSTP         increment stack pointer
  336. 9B DB E3     FINIT           initialize 80287
  337.  w DF /2     FIST mem2i      mem2i := 0
  338.  w DB /2     FIST mem4i      mem4i := 0
  339.  w DF /3     FISTP mem2i     mem2i := 0, pop
  340.  w DB /3     FISTP mem4i     mem4i := 0, pop
  341.  w DF /7     FISTP mem8i     mem8i := 0, pop
  342.  w DE /4     FISUB mem2i     0 := 0 - mem2i
  343.  w DA /4     FISUB mem4i     0 := 0 - mem4i
  344.  w DE /5     FISUBR mem2i    0 := mem2i - 0
  345.  w DA /5     FISUBR mem4i    0 := mem4i - 0
  346.  w D9 C0+i   FLD i           push, 0 := old i
  347.  w DB /5     FLD mem10r      push, 0 := mem10r
  348.  w D9 /0     FLD mem4r       push, 0 := mem4r
  349.  w DD /0     FLD mem8r       push, 0 := mem8r
  350.  w D9 E8     FLD1            push, 0 := 1.0
  351.  w D9 /5     FLDCW mem2i     control word := mem2i
  352.  w D9 /4     FLDENV mem14    environment := mem14
  353.  w D9 EA     FLDL2E          push, 0 := log base 2.0 of e
  354.  w D9 E9     FLDL2T          push, 0 := log base 2.0 of 10.0
  355.  w D9 EC     FLDLG2          push, 0 := log base 10.0 of 2.0
  356.  w D9 ED     FLDLN2          push, 0 := log base e of 2.0
  357.  w D9 EB     FLDPI           push, 0 := Pi
  358.  w D9 EE     FLDZ            push, 0 := +0.0
  359.  w DE C9     FMUL            1 := 1 * 0, pop
  360.  w D8 C8+i   FMUL i          0 := 0 * i
  361.  w DC C8+i   FMUL i,0        i := i * 0
  362.  w D8 C8+i   FMUL 0,i        0 := 0 * i
  363.  w D8 /1     FMUL mem4r      0 := 0 * mem4r
  364.  w DC /1     FMUL mem8r      0 := 0 * mem8r
  365.  w DE C8+i   FMULP i,0       i := i * 0, pop
  366.    DB E2     FNCLEX          nowait clear exceptions
  367.    DB E1     FNDISI          disable interrupts (.287 ignore)
  368.    DB E0     FNENI           enable interrupts (.287 ignore)
  369.    DB E3     FNINIT          nowait initialize 80287
  370.  w D9 D0     FNOP            no operation
  371.    DD /6     FNSAVE mem94    mem94 := 80287 state
  372.    D9 /7     FNSTCW mem2i    mem2i := control word
  373.    D9 /6     FNSTENV mem14   mem14 := environment
  374.    DF E0     FNSTSW AX       AX := status word
  375.    DD /7     FNSTSW mem2i    mem2i := status word
  376.  w D9 F3     FPATAN          0 := arctan(1/0), pop
  377.  w D9 F8     FPREM           0 := REPEAT(0 - 1)
  378.  w D9 F2     FPTAN           push, 1/0 := tan(old 0)
  379.  w D9 FC     FRNDINT         0 := round(0)
  380.  w DD /4     FRSTOR mem94    80287 state := mem94
  381.  w DD /6     FSAVE mem94     mem94 := 80287 state
  382.  w D9 FD     FSCALE          0 := 0 * 2.0 ** 1
  383. 9B DB E4     FSETPM          set protection mode
  384.  w D9 FA     FSQRT           0 := square root of 0
  385.                                                           7-8
  386.  
  387.  w DD D0+i   FST i           i := 0
  388.  w D9 /2     FST mem4r       mem4r := 0
  389.  w DD /2     FST mem8r       mem8r := 0
  390.  w D9 /7     FSTCW mem2i     mem2i := control word
  391.  w D9 /6     FSTENV mem14    mem14 := environment
  392.  w DD D8+i   FSTP i          i := 0, pop
  393.  w DB /7     FSTP mem10r     mem10r := 0, pop
  394.  w D9 /3     FSTP mem4r      mem4r := 0, pop
  395.  w DD /3     FSTP mem8r      mem8r := 0, pop
  396.  w DF E0     FSTSW AX        AX := status word
  397.  w DD /7     FSTSW mem2i     mem2i := status word
  398.  w DE E9     FSUB            1 := 1 - 0, pop
  399.  w D8 E0+i   FSUB i          0 := 0 - i
  400.  w DC E8+i   FSUB i,0        i := i - 0
  401.  w D8 E0+i   FSUB 0,i        0 := 0 - i
  402.  w D8 /4     FSUB mem4r      0 := 0 - mem4r
  403.  w DC /4     FSUB mem8r      0 := 0 - mem8r
  404.  w DE E8+i   FSUBP i,0       i := i - 0
  405.  w DE E1     FSUBR           1 := 0 - 1, pop
  406.  w D8 E8+i   FSUBR i         0 := i - 0
  407.  w DC E0+i   FSUBR i,0       i := 0 - i
  408.  w D8 E8+i   FSUBR 0,i       0 := i - 0
  409.  w D8 /5     FSUBR mem4r     0 := mem4r - 0
  410.  w DC /5     FSUBR mem8r     0 := mem8r - 0
  411.  w DE E0+i   FSUBRP i,0      i := 0 - i, pop
  412.  w D9 E4     FTST            compare 0 - 0.0
  413. 9B           FWAIT           wait for 80287 ready
  414.  w D9 E5     FXAM            C3 -- C0 := type of 0
  415.  w D9 C9     FXCH            exchange 0 and 1
  416.  w D9 C8+i   FXCH 0,i        exchange 0 and i
  417.  w D9 C8+i   FXCH i          exchange 0 and i
  418.  w D9 C8+i   FXCH i,0        exchange 0 and i
  419.  w D9 F4     FXTRACT         push, 1 := expo, 0 := sig
  420.  w D9 F1     FYL2X           0 := 1 * log base 2.0 of 0, pop
  421.  w D9 F9     FYL2XP1         0 := 1 * log base 2.0 of (0+1.0), pop
  422.  
  423.